<template>
  <q-field ref="QMonth" v-model="monthVal" :autogrow="false" :class="fieldCls" :disable="disable" :error="!isValid"
           :error-message="placeholder" :readonly="readonly" class="q-field__text" lazy-rules="ondemand" v-bind="$attrs"
           @focus="fieldFocus">
    <template v-slot:control>
      <span v-if="!showText" class="placeholder">{{ placeholder }}</span>
      <QxTooltip v-else>{{ showText }}</QxTooltip>
    </template>
    <template v-slot:append>
      <q-icon v-if="showClearIcon" class="field-clear-icon cursor-pointer" name="cancel"
              @click.stop="handleClear"></q-icon>
      <q-icon name="calendar_month"></q-icon>
    </template>
    <q-popup-proxy v-model="showPopup" :no-parent-event="readonly || disable" :offset="[0, 5]" anchor="bottom left"
                   breakpoint="0" class="q-month column justify-around items-start overflow-hidden" no-focus no-refocus
                   self="top left" transition-hide="jump-up" transition-show="jump-down" @hide="popupBlur">
      <div class="q-month--header full-width row justify-around items-start">
        <q-btn dense flat icon="chevron_left" round @click="prevPage"></q-btn>
        <q-btn class="text-bold text-body1" dense flat>{{ startYear }}</q-btn>
        <q-btn dense flat icon="chevron_right" round @click="nextPage"></q-btn>
      </div>
      <transition :name="`q-transition--slide-${moveDir}`">
        <div :key="startYear" class="q-month--view self-stretch justify-around row items-center">
          <template v-for="i in 12" :key="i">
            <q-btn :class="monthCls(i)" :disable="canSelect(i)" v-bind="btnBind" @click="updateValue(i)"
                   @mouseenter="onMouseoverEnv(i)">
              {{ $t(`M${i}`) }}
            </q-btn>
          </template>
        </div>
      </transition>
    </q-popup-proxy>
  </q-field>
</template>

<script setup>
import QxTooltip from 'components/Global/Tools/QxTooltip.vue'
import { date, format } from 'quasar'
import { getTypeof, isEmpty } from 'src/utils/utils'
import { computed, inject, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'

const YEAR = new Date().getFullYear()

const props = defineProps({
  type: String, disable: Boolean, readonly: Boolean, multiple: Boolean, placeholder: String, option: [Array, Function],
  lazyRules: [Boolean, String], clearable: { type: Boolean, default: true },
  modelValue: [String, Date, Array, Number, Object]
})

const BgToColor = inject('BgToColor')
const emits = defineEmits(['change', 'update:modelValue'])

const startYear = ref(YEAR)
const QMonth = ref(null)
const isValid = ref(true)
const hoverMonth = ref(0)
const monthVal = ref(void 0)
const showPopup = ref(false)
const moveDir = ref('right')

const btnBind = reactive({ class: 'col-2', dense: true, flat: true })
const fieldCls = computed(() => showPopup.value ? 'q-field--focused q-field--highlighted' : '')

const dateToNum = dateVal => +dateVal.replace('-', '')
const formatInitYear = dateVal => +date.formatDate(dateVal, 'YYYY')

const showClearIcon = computed(() => {
  const { clearable, disable, readonly } = props
  if (readonly || disable || !clearable) return false
  return !!monthVal.value
})
const monthCls = computed(() => {
  const yearMonth = +date.formatDate(new Date(), 'YYYYMM')
  return index => {
    const monthStr = `${startYear.value}-${format.pad(index, 2)}`
    const month = dateToNum(monthStr)
    if (isEmpty(monthVal.value)) return month === yearMonth ? 'text-bold text-primary' : ''
    if (getTypeof(monthVal.value) === 'array') {
      if (monthVal.value.includes(monthStr)) return `bg-primary text-bold text-${BgToColor('primary')}`
      return
    }
    if (getTypeof(monthVal.value) === 'object') {
      if (hoverMonth.value) {
        const tempMonth = dateToNum(monthVal.value.from)
        if (tempMonth === month) {
          if (tempMonth < hoverMonth.value) {
            return 'text-primary q-month__start_hover_month q-month__hover_month q-month__start_month'
          }
          if (tempMonth > hoverMonth.value) {
            return 'text-primary q-month__end_hover_month q-month__hover_month q-month__end_month'
          }
          return 'text-primary q-month__hover_month'
        }
        if (tempMonth < month) {
          if (month < hoverMonth.value) return 'text-primary no-border-radius q-month__hover_month'
          if (month === hoverMonth.value) {
            return 'text-primary q-month__end_hover_month q-month__hover_month q-month__end_month'
          }
          return
        }
        if (month > hoverMonth.value) return 'text-primary no-border-radius q-month__hover_month'
        if (month === hoverMonth.value) {
          return 'text-primary q-month__start_hover_month q-month__hover_month q-month__start_month'
        }
        return
      }
      if (monthVal.value.from && monthVal.value.to) {
        const sYear = dateToNum(monthVal.value.from)
        const eYear = dateToNum(monthVal.value.to)
        if (sYear === month && eYear === month) return `bg-primary text-bold text-${BgToColor('primary')}`
        if (sYear === month) {
          return `bg-primary text-bold text-${BgToColor('primary')} q-month__start_month`
        }
        if (eYear === month) {
          return `bg-primary text-bold text-${BgToColor('primary')} q-month__end_month`
        }
        if (month > sYear && month < eYear) return 'no-border-radius q-month__range'
      }
    }
    if (monthVal.value === monthStr) return `bg-primary text-bold text-${BgToColor('primary')}`
    if (month === yearMonth) return 'text-primary text-bold'
  }
})
const range = computed(() => props.multiple ? false : (props.type || '').includes('range'))
const showText = computed(() => {
  if (isEmpty(monthVal.value)) return ''
  if (getTypeof(monthVal.value) === 'array') return monthVal.value.join(', ')
  if (getTypeof(monthVal.value) === 'object') return `${monthVal.value.from || 'NA'}/${monthVal.value.to || 'NA'}`
  return monthVal.value
})

const prevPage = () => {
  startYear.value -= 1
  moveDir.value = 'right'
}
const nextPage = () => {
  startYear.value += 1
  moveDir.value = 'left'
}
const initYear = () => {
  if (isEmpty(monthVal.value)) return (startYear.value = YEAR)
  if (getTypeof(monthVal.value) === 'array') return (startYear.value = formatInitYear(monthVal.value[0]))
  if (getTypeof(monthVal.value) === 'object') return (startYear.value = formatInitYear(monthVal.value.from))
  startYear.value = formatInitYear(monthVal.value)
}
const initValue = () => {
  if (monthVal.value === props.modelValue) return
  if (isEmpty(props.modelValue)) return (monthVal.value = void 0)
  if (getTypeof(props.modelValue) === 'date') return (monthVal.value =
    date.formatDate(new Date(props.modelValue), 'YYYY-MM'))
  monthVal.value = props.modelValue
}

watch(() => props.modelValue, () => initValue(), { immediate: true })

const popupBlur = () => {
  if (range.value && monthVal.value && !monthVal.value.to) monthVal.value = void 0
  if (props.multiple) emits('update:modelValue', monthVal.value || '')
  if (props.lazyRules === 'ondemand') return
  nextTick(() => {
    if (!QMonth.value) return
    QMonth.value.blur()
    isValid.value = QMonth.value.validate()
  })
}
const handleClear = () => {
  monthVal.value = void 0
  showPopup.value = false
  popupBlur()
}
const fieldFocus = () => initYear()
const onMouseoverEnv = e => {
  if (!range.value) return
  if (isEmpty(monthVal.value)) return
  if (getTypeof(monthVal.value) !== 'object') return
  if (monthVal.value.to) return
  hoverMonth.value = dateToNum(`${startYear.value}-${format.pad(e, 2)}`)
}
const canSelect = computed(() => {
  return month => {
    const { option } = props
    if (!option) return false
    month = `${startYear.value}-${format.pad(month, 2)}`
    if (typeof option === 'function') return !option(month)
    else if (getTypeof(option) === 'array') return !option.includes(month)
  }
})
const updateValue = month => {
  month = `${startYear.value}-${format.pad(month, 2)}`
  if (props.multiple) {
    if (isEmpty(monthVal.value)) monthVal.value = [month]
    else monthVal.value.push(month)
    emits('change', monthVal.value, month)
    return
  }
  if (range.value) {
    if (isEmpty(monthVal.value) || monthVal.value.to) {
      hoverMonth.value = month
      monthVal.value = { from: month }
      return
    } else {
      if (month < monthVal.value.from) {
        monthVal.value.to = monthVal.value.from
        monthVal.value.from = month
      } else monthVal.value.to = month
      hoverMonth.value = 0
      showPopup.value = false
      emits('update:modelValue', monthVal.value || '')
      emits('change', monthVal.value, month)
    }
    return
  }
  monthVal.value = month
  showPopup.value = false
  emits('update:modelValue', monthVal.value || '')
  emits('change', month)
}

const scrollHandle = () => {
  if (!showPopup.value || !QMonth.value) return
  const offset = QMonth.value.$el.getBoundingClientRect()
  const innerHeight = window.innerHeight
  if (offset.top > innerHeight || offset.bottom < 0) showPopup.value = false
}
const resizeHandle = () => (showPopup.value = false)
onMounted(() => {
  window.addEventListener('scroll', scrollHandle, true)
  window.addEventListener('resize', resizeHandle, false)
})
onBeforeUnmount(() => {
  window.removeEventListener('scroll', scrollHandle, true)
  window.removeEventListener('resize', resizeHandle, false)
})
</script>